#ifndef RISCV_H
#define RISCV_H

#include "types.h"

//读取当前硬件线程的ID
static inline uint64 r_mhartid() {
  uint64 x;
  asm volatile("csrr %0, mhartid" : "=r"(x));
  return x;
}

//读取 supervisor 状态寄存器
static inline uint64 r_sstatus() {
  uint64 x;
  asm volatile("csrr %0, sstatus" : "=r"(x));
  return x;
}

//写入值到 sstatus 寄存器
static inline void w_sstatus(uint64 x) {
  asm volatile("csrw sstatus, %0" : : "r"(x));
}

//写入值到 sepc 寄存器（supervisor 异常程序计数器），设置异常处理完成后返回的地址。
static inline void w_sepc(uint64 x) {
  asm volatile("csrw sepc, %0" : : "r"(x));
}

//读取 sepc 寄存器的值，获取触发异常时的指令地址
static inline uint64 r_sepc() {
  uint64 x;
  asm volatile("csrr %0, sepc" : "=r"(x));
  return x;
}

//写入值到 stvec 寄存器（supervisor 异常向量表基址），设置异常处理程序的入口地址
static inline void w_stvec(uint64 x) {
  asm volatile("csrw stvec, %0" : : "r"(x));
}

//定义 SV39 页表模式
#define SATP_SV39 (8L << 60)
//将页表物理地址转换为 satp 寄存器的值
#define MAKE_SATP(pagetable) (SATP_SV39 | (((uint64)pagetable) >> 12))

//写入值到 satp 寄存器（supervisor 地址转换与保护），启用 / 切换页表
static inline void w_satp(uint64 x) {
  asm volatile("csrw satp, %0" : : "r"(x));
}

//读取 satp 寄存器的值，获取当前使用的页表信息。
static inline uint64 r_satp() {
  uint64 x;
  asm volatile("csrr %0, satp" : "=r"(x));
  return x;
}

//读取 scause 寄存器，获取异常 / 中断的原因（如页故障、时钟中断）
static inline uint64 r_scause() {
  uint64 x;
  asm volatile("csrr %0, scause" : "=r"(x));
  return x;
}

//读取 stval 寄存器，获取异常相关的附加信息（如页故障的地址）
static inline uint64 r_stval() {
  uint64 x;
  asm volatile("csrr %0, stval" : "=r"(x));
  return x;
}

//执行虚拟内存地址转换栅栏指令，刷新 TLB
static inline void sfence_vma() {
  asm volatile("sfence.vma zero, zero");
}

//定义sstatus 寄存器标志位
#define SSTATUS_SIE (1L << 1)   //允许 supervisor 级中断
#define SSTATUS_SPIE (1L << 5)  //保存中断使能状态
#define SSTATUS_SPP (1L << 8)   //指示异常前的特权级,0 = 用户态，1 = 内核态

//定义页对齐宏
#define PGSHIFT 12              //页偏移位数
#define PGROUNDUP(sz) (((sz) + PGSIZE - 1) & ~(PGSIZE - 1)) //将 sz 向上对齐到最近的页边界
#define PGROUNDDOWN(a) ((a) & ~(PGSIZE - 1)) //将地址 a 向下对齐到最近的页边界

//定义页表项（PTE）标志
#define PTE_V (1L << 0)         //有效位
#define PTE_R (1L << 1)         //read
#define PTE_W (1L << 2)         //write
#define PTE_X (1L << 3)         //执行
#define PTE_U (1L << 4)         //允许用户访问

//物理地址转换_PTE
#define PA2PTE(pa) ((((uint64)(pa)) >> 12) << 10)  //将物理地址（pa）转换为 PTE 中的物理页号部分
#define PTE2PA(pte) (((pte) >> 10) << 12)          //从 PTE 中提取物理地址
#define PTE_FLAGS(pte) ((pte) & 0x3FF)             //提取 PTE 的低 10 位标志

//特表索引计算
#define PXMASK 0x1FF                               //页表索引掩码
#define PXSHIFT(level) (PGSHIFT + 9 * (level))     //计算某级页表索引的偏移位
#define PX(level, va) ((((uint64)(va)) >> PXSHIFT(level)) & PXMASK)  //从虚拟地址（va）中提取第level级页表的索引值

#endif